home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / tde11.arc / ED.C < prev    next >
C/C++ Source or Header  |  1991-09-06  |  41KB  |  1,360 lines

  1. /*******************  start of original comments  ********************/
  2. /*
  3.  * Written by Douglas Thomson (1989/1990)
  4.  *
  5.  * This source code is released into the public domain.
  6.  */
  7.  
  8. /*
  9.  * Name:    dte - Doug's Text Editor program - main editor module
  10.  * Purpose: This file contains the main editor module, and a number of the
  11.  *           smaller miscellaneous editing commands.
  12.  *          It also contains the code for dispatching commands.
  13.  * File:    ed.c
  14.  * Author:  Douglas Thomson
  15.  * System:  this file is intended to be system-independent
  16.  * Date:    October 1, 1989
  17.  * I/O:     file being edited
  18.  *          files read or written
  19.  *          user commands and prompts
  20.  * Notes:   see the file "dte.doc" for general program documentation
  21.  */
  22. /*********************  end of original comments   ********************/
  23.  
  24. /*
  25.  * The basic editor routines have been EXTENSIVELY reworked.  I have added
  26.  * support for lines longer than 80 columns and I have added line number
  27.  * support.  I like to know the real line number that editor functions are
  28.  * working on and I like to know the total number of lines in a file.
  29.  *
  30.  * I rewrote the big series of ifs in the dispatch subroutine.  It is now
  31.  * a series of case statements.  By doing so, it should be easier to implement a
  32.  * configuration file later on.
  33.  *
  34.  * I added a few functions that I use quite often and I deleted a few that I
  35.  * rarely use.  Added are Split Line, Join Line, and Duplicate Line.   Deleted
  36.  * are Word Delete, Goto Marker 0-9 (others?).
  37.  *
  38.  * I felt that the insert routine should be separated into two routines.  One
  39.  * for inserting the various combinations of newlines and one for inserting
  40.  * 'regular' text characters.
  41.  *
  42.  * One of Doug's design considerations was keeping screen updates to a minimum.
  43.  * I have expanded upon that idea and added support for updating windows
  44.  * LOCALly, GLOBALly, or NOT_LOCALly.  For example, scrolling in one window
  45.  * does not affect the text in another window - LOCAL update.  Adding, deleting,
  46.  * or modifying text in one window may affect text in other windows - GLOBAL
  47.  * update.  Sometimes, updates to the current window are handled in the task
  48.  * routines so updates to other windows are done NOT_LOCALly.
  49.  *
  50.  * Also note that using functions copy_line and un_copy_line to change a line
  51.  * automatically adjusts the g_status.end_mem pointer.  If a function bypasses
  52.  * those functions, adjusting the g_status.end_mem pointer must be done
  53.  * explicitly.
  54.  *
  55.  * New editor name:  tde, the Thomson-Davis Editor.
  56.  * Author:           Frank Davis
  57.  * Date:             June 5, 1991
  58.  *
  59.  * This modification of Douglas Thomson's code is released into the
  60.  * public domain, Frank Davis.   You may distribute it freely.
  61.  */
  62.  
  63. #include "tdestr.h"     /* global variables */
  64. #include "global.h"     /* global variables */
  65. #include "define.h"
  66. #include "tdefunc.h"
  67.  
  68. /*
  69.  * Name:    tab_key
  70.  * Purpose: To make the necessary changes after the user types the tab key.
  71.  * Date:    June 5, 1991
  72.  * Passed:  window: information allowing access to the current window
  73.  * Notes:   If in insert mode, then this function adds the required
  74.  *           number of spaces in the file.
  75.  *          If not in insert mode, then tab simply moves the cursor right
  76.  *           the required distance.
  77.  */
  78. void tab_key( window )
  79. windows *window;
  80. {
  81. int spaces;  /* the spaces to move to the next tab stop */
  82. text_ptr source;    /* source for block move to make room for c */
  83. text_ptr dest;      /* destination for block move */
  84. long number;        /* number of characters to be moved */
  85. int space, pad, add, len, i, rcol, ccol;
  86. file_infos *file;
  87. int prompt_line;
  88.  
  89.    prompt_line = window->bottom_line;
  90.    rcol = window->rcol;
  91.    ccol = window->ccol;
  92.    /*
  93.     * work out the number of spaces to the next tab stop
  94.     */
  95.    spaces = g_status.tab_size - (rcol % g_status.tab_size);
  96.  
  97.    if (g_status.insert && rcol + spaces < g_display.line_length) {
  98.       copy_line( window->cursor, prompt_line );
  99.       /*
  100.        * work out how many characters need to be inserted
  101.        */
  102.       len = linelen( g_status.line_buff );
  103.       if (rcol > len)   /* padding required */
  104.          pad = rcol - len;
  105.       else
  106.          pad = 0;
  107.       if (g_status.line_buff[len] == CONTROL_Z)
  108.          ++pad;
  109.       if (len + pad + spaces >= g_display.line_length)
  110.          error( WARNING, window->bottom_line, "line too long to add" );
  111.       else {
  112.          file = window->file_info;
  113.          space = 0;
  114.          if (pad > 0  || spaces > 0) {
  115.             space = pad + spaces;
  116.             if (g_status.line_buff[len] == CONTROL_Z) {
  117.                g_status.line_buff[len] = '\n';
  118.                g_status.line_buff[len+1] = CONTROL_Z;
  119.                ++file->length;
  120.                show_size( window );
  121.                --pad;
  122.                ++len;
  123.             }
  124.             source = g_status.line_buff + rcol - pad;
  125.             dest = source + pad + spaces;
  126.             len = len + pad - rcol + 2;
  127.             memmove( dest, source, len );
  128.  
  129.             /*
  130.              * if padding was required, then put in the required spaces
  131.              */
  132.             for (i=pad; i>0; i--)
  133.                *source++ = ' ';
  134.             for (i=spaces; i>0; i--)
  135.                *source++ = ' ';
  136.          }
  137.  
  138.          file->dirty = GLOBAL;
  139.          show_changed_line( window );
  140.          rcol += spaces;
  141.          ccol += spaces;
  142.       }
  143.    } else if (rcol + spaces <= g_display.line_length) {
  144.       /*
  145.        * advance the cursor without changing the text underneath
  146.        */
  147.       rcol += spaces;
  148.       ccol += spaces;
  149.    }
  150.    check_virtual_col( window, rcol, ccol );
  151. }
  152.  
  153.  
  154. /*
  155.  * Name:    insert_newline
  156.  * Purpose: insert a newline
  157.  * Date:    June 5, 1991
  158.  * Passed:  window:   information allowing access to the current window
  159.  *          carriage_return:   TRUE if carriage return, FALSE if insert line
  160.  *          split_line:   TRUE if split line, FALSE otherwise
  161.  */
  162. void insert_newline( window, carriage_return, split_line )
  163. windows *window;
  164. int carriage_return, split_line;
  165. {
  166. text_ptr source;    /* source for block move to make room for c */
  167. text_ptr dest;      /* destination for block move */
  168. long number;        /* number of characters to be moved */
  169. int len;            /* length of current line */
  170. int pad;            /* padding to add if cursor beyond end of line */
  171. int add;            /* characters to be added (usually 1 in insert mode) */
  172. int i;              /* counter for adding autoindenting */
  173. int rcol;
  174. text_ptr prev;      /* previous lines scanned for autoindent */
  175. int space;
  176. char *trail_space;
  177. file_infos *file;
  178. int prompt_line;
  179.  
  180.    prompt_line = window->bottom_line;
  181.    file = window->file_info;
  182.    window->cursor = cpf( window->cursor );
  183.    copy_line( window->cursor, prompt_line );
  184.    len = linelen( g_status.line_buff );
  185.  
  186.    source = g_status.line_buff + len;
  187.    if (carriage_return || split_line) {
  188.       if (window->rcol < len)
  189.          source = g_status.line_buff + window->rcol;
  190.    }
  191.    /*
  192.     *  make room for '\n' just after source (source+1)
  193.     */
  194.    memmove( source+1, source, linelen( source )+2 );
  195.  
  196.    *source = '\n';
  197.    un_copy_line( window->cursor, window, TRUE );
  198.    adjust_windows_cursor( window, 1 );
  199.  
  200.    if (carriage_return || split_line)
  201.       update_line( window );
  202.  
  203.    if  (window->cline == window->bottom_line)
  204.        window_scroll_up( window->top_line, window->bottom_line );
  205.    else
  206.        window_scroll_down( window->cline+1, window->bottom_line );
  207.  
  208.    /*
  209.     * If the cursor is to move down to the next line, then update
  210.     *  the line and column appropriately.
  211.     */
  212.    file->dirty = NOT_LOCAL;
  213.    if (carriage_return || split_line) {
  214.       window->cursor = find_next( window->cursor );
  215.       if (window->cline < window->bottom_line)
  216.          window->cline++;
  217.       window->rline++;
  218.       rcol = window->rcol;
  219.  
  220.       /*
  221.        * indentation is only required if we are in the right mode,
  222.        *  the user typed <CR>, and if there is not space followed
  223.        *  by something after the cursor.
  224.        */
  225.       if (g_status.indent) {
  226.          /*
  227.           * autoindentation is required. Match the indentation of
  228.           *  the first line above that is not blank.
  229.           */
  230.          add = first_non_blank( g_status.line_buff );
  231.          if (g_status.line_buff[add] == '\n' ||
  232.                    g_status.line_buff[add] == CONTROL_Z) {
  233.             prev = cpb( window->cursor );
  234.             while ((prev = find_prev( prev )) != NULL) {
  235.                add = first_non_blank( (char *)prev );
  236.                if (prev[add] != '\n')
  237.                   break;
  238.             }
  239.          }
  240.          copy_line( window->cursor, prompt_line );
  241.          len = linelen( g_status.line_buff );
  242.          source = g_status.line_buff;
  243.          dest = source + add;
  244.          memmove( dest, source, len+add+2 );
  245.  
  246.          /*
  247.           * now put in the autoindent characters
  248.           */
  249.          for (i=add; i>0; i--)
  250.             *source++ = ' ';
  251.  
  252.          window->rcol = add;
  253.          un_copy_line( window->cursor, window, TRUE );
  254.          update_line( window );
  255.       } else
  256.          window->rcol = 0;
  257.       if (split_line) {
  258.          window->cursor = cpb( window->cursor );
  259.          window->cursor = find_prev( window->cursor );
  260.          if (window->cline > window->top_line)
  261.             window->cline--;
  262.          window->rline--;
  263.          window->rcol = rcol;
  264.       }
  265.       check_virtual_col( window, window->rcol, window->ccol );
  266.       if (file->dirty)
  267.          file->dirty = GLOBAL;
  268.    }
  269.  
  270.    /*
  271.     * record that file has been modified
  272.     */
  273.    ++file->length;
  274.    restore_marked_block( window, 1 );
  275.    show_size( window );
  276. }
  277.  
  278.  
  279. /*
  280.  * Name:    insert_overwrite
  281.  * Purpose: To make the necessary changes after the user has typed a normal
  282.  *           printable character
  283.  * Date:    June 5, 1991
  284.  * Passed:  window:   information allowing access to the current window
  285.  *          c:        the character just typed
  286.  */
  287. void insert_overwrite( window, c )
  288. windows *window;
  289. int c;
  290. {
  291. text_ptr source;    /* source for block move to make room for c */
  292. text_ptr dest;      /* destination for block move */
  293. long number;        /* number of characters to be moved */
  294. int len;            /* length of current line */
  295. int pad;            /* padding to add if cursor beyond end of line */
  296. int i;
  297. int add;            /* characters to be added (usually 1 in insert mode) */
  298. int line;           /* line on screen to save and show prompt */
  299. int space, rcol, ccol;
  300. file_infos *file;
  301.  
  302.    line = window->bottom_line;
  303.    rcol = window->rcol;
  304.    ccol = window->ccol;
  305.    /*
  306.     * first check we have room - the editor can not
  307.     *  cope with lines wider than g_display.line_length
  308.     */
  309.    if (rcol >= g_display.line_length)
  310.       error( WARNING, line, "cannot insert more characters" );
  311.    else {
  312.       file = window->file_info;
  313.       copy_line( window->cursor, line );
  314.  
  315.       /*
  316.        * work out how many characters need to be inserted
  317.        */
  318.       len = linelen( g_status.line_buff );
  319.       if (rcol > len)   /* padding required */
  320.          pad = rcol - len;
  321.       else
  322.          pad = 0;
  323.  
  324.       /*
  325.        * if this is the last line in a file, the last character in the
  326.        * line buffer will be CONTROL_Z.  increment pad and insert a \n.
  327.        */
  328.       if (g_status.line_buff[len] == CONTROL_Z)
  329.          ++pad;
  330.  
  331.       if (g_status.insert || rcol >= len)
  332.          /*
  333.           * inserted characters, or overwritten characters at the end of
  334.           *  the line, are inserted.
  335.           */
  336.          add = 1;
  337.       else
  338.          /*
  339.           *  and no extra space is required to overwrite existing characters
  340.           */
  341.          add = 0;
  342.  
  343.       /*
  344.        * check that current line would not get too long. Note that there must
  345.        *  be space for both the old line and any indentation.
  346.        */
  347.       if (len + pad + add >= g_display.line_length)
  348.          error( WARNING, line, "no more room to add" );
  349.       else {
  350.          /*
  351.           * all clear to add new character!
  352.           */
  353.  
  354.          /*
  355.           * move character to make room for whatever needs to be inserted
  356.           */
  357.          space = 0;
  358.          if (pad > 0  || add > 0) {
  359.             space = pad + add;
  360.             if (g_status.line_buff[len] == CONTROL_Z) {
  361.                if (rcol > len) {
  362.                   g_status.line_buff[rcol+1] = '\n';
  363.                   g_status.line_buff[rcol+2] = CONTROL_Z;
  364.                } else {
  365.                   g_status.line_buff[len] = '\n';
  366.                   g_status.line_buff[len+1] = CONTROL_Z;
  367.                }
  368.                ++file->length;
  369.                show_size( window );
  370.                --pad;
  371.                ++len;
  372.             }
  373.             source = g_status.line_buff + rcol - pad;
  374.             dest = source + pad + add;
  375.             len = len + pad - rcol + 2;
  376.             memmove( dest, source, len );
  377.             /*
  378.              * if padding was required, then put in the required spaces
  379.              */
  380.             for (i=pad; i>0; i--)
  381.                *source++ = ' ';
  382.          } else
  383.             source = g_status.line_buff + rcol;
  384.          /*
  385.           * now place the new character
  386.           */
  387.          *source = c;
  388.  
  389.          /*
  390.           * if we added anything, show the changed line.
  391.           */
  392.          if (pad > 0 || add > 0)
  393.             update_line( window );
  394.          else
  395.             update_char( window, c, ccol, window->cline );
  396.  
  397.          /*
  398.           * always increment the real column (rcol) and adjust the
  399.           * logical and base column as needed.
  400.           */
  401.          file->dirty = GLOBAL;
  402.          if (ccol < g_display.ncols - 1) {
  403.             ccol++;
  404.             show_changed_line( window );
  405.          } else
  406.             window->bcol++;
  407.          rcol++;
  408.  
  409.          /*
  410.           * record that file has been modified and adjust cursors and
  411.           * file start and end markers as needed.
  412.           */
  413.       }
  414.       window->rcol = rcol;
  415.       window->ccol = ccol;
  416.    }
  417. }
  418.  
  419.  
  420. /*
  421.  * Name:    join_line
  422.  * Purpose: To join current line and line below at cursor
  423.  * Date:    June 5, 1991
  424.  * Passed:  window:   information allowing access to the current window
  425.  * Notes:   trunc the line then join with line below if it exists
  426.  */
  427. void join_line( window )
  428. windows *window;
  429. {
  430. int len;            /* length of current line */
  431. text_ptr source;    /* source for block move to delete word */
  432. text_ptr dest;      /* destination for block move */
  433. long number;        /* number of characters to move */
  434. int del_len;        /* number of characters to delete */
  435. text_ptr p;         /* next line in file */
  436. int pad, i;         /* padding spaces required */
  437. int cr;             /* does current line end with carriage return? */
  438. int line;           /* line on screen to save and show prompt */
  439. file_infos *file;
  440. int rcol;
  441.  
  442.    file = window->file_info;
  443.    line = window->bottom_line;
  444.    rcol = window->rcol;
  445.  
  446.    window->cursor = cpf( window->cursor );
  447.    copy_line( window->cursor, window->bottom_line );
  448.    if (rcol < (len = linelen( g_status.line_buff ))) {
  449.       /*
  450.        * delete rest of line
  451.        */
  452.       dest = g_status.line_buff + rcol;
  453.  
  454.       /*
  455.        * the \n at the end of the line must NOT be deleted
  456.        */
  457.       if (g_status.line_buff[len] == '\n')
  458.          *dest++ = '\n';
  459.  
  460.       len = find_CONTROL_Z( dest );
  461.       *dest = CONTROL_Z;
  462.       un_copy_line( window->cursor, window, FALSE );
  463.       file->dirty = GLOBAL;
  464.    }
  465.    /*
  466.     * we need to combine with the next line, if any
  467.     */
  468.    if ((p = find_next( window->cursor )) != NULL) {
  469.       /*
  470.        * add padding if required
  471.        */
  472.       len = linelen( g_status.line_buff );
  473.       if (g_status.line_buff[len] == '\n')
  474.          cr = 1;
  475.       else
  476.          cr = 0;
  477.       if (rcol > len)
  478.          pad = rcol - len;
  479.       else
  480.          pad = 0;
  481.  
  482.       /*
  483.        * check room to combine lines
  484.        */
  485.       if (len + pad + cr + linelen( p ) >= g_display.line_length)
  486.          error( WARNING, line, "cannot combine lines" );
  487.       else {
  488.  
  489.          /*
  490.           * do the move
  491.           */
  492.          source = g_status.line_buff + rcol - pad;
  493.          dest = source + pad;
  494.          len = len + pad - rcol + 1 + cr;
  495.          memmove( dest, source, len );
  496.  
  497.          /*
  498.           * insert the padding
  499.           */
  500.          for (i=pad; i>0; i--)
  501.             *source++ = ' ';
  502.  
  503.          /*
  504.           * remove the \n separating the two lines.
  505.           */
  506.          i = 0;
  507.          if (*source == '\n') {
  508.             *source = CONTROL_Z;
  509.             i = -1;
  510.          }
  511.          g_status.copied = TRUE;
  512.          un_copy_line( window->cursor, window, FALSE );
  513.          adjust_windows_cursor( window, i );
  514.          --file->length;
  515.          restore_marked_block( window, -1 );
  516.          show_size( window );
  517.          file->dirty = GLOBAL;
  518.       }
  519.    }
  520. }
  521.  
  522.  
  523. /*
  524.  * Name:    dup_line
  525.  * Purpose: Duplicate current line
  526.  * Date:    June 5, 1991
  527.  * Passed:  window:   information allowing access to the current window
  528.  * Notes:   cursor stays on current line
  529.  */
  530. void dup_line( window )
  531. windows *window;
  532. {
  533. int len, i;         /* length of current line */
  534. int line;           /* line on screen to save and show prompt */
  535. long number;
  536. file_infos *file;
  537. text_ptr d, s;
  538.  
  539.    line = window->bottom_line;
  540.    window->cursor = cpf( window->cursor );
  541.  
  542.    un_copy_line( window->cursor, window, TRUE );
  543.    /*
  544.     * don't dup the ^Z or a NULL line
  545.     */
  546.    if (*window->cursor != CONTROL_Z && (d=find_next( window->cursor )) !=NULL) {
  547.       file = window->file_info;
  548.  
  549.       /*
  550.        * don't use buffers to dup the line.  use hw_move to make space and
  551.        * copy current line at same time.  d is set to beginning of next line.
  552.        */
  553.       s = window->cursor;
  554.       len = linelen( s );
  555.       if (s[len] == '\n')
  556.          ++len;
  557.       number = ptoul( g_status.end_mem ) - ptoul( s );
  558.       hw_move( d, s, number );
  559.       g_status.end_mem = addltop( len, g_status.end_mem );
  560.       adjust_start_end( file, len );
  561.       addorsub_all_cursors( window, len );
  562.       adjust_windows_cursor( window, 1 );
  563.  
  564.       /*
  565.        * if current line is the bottom line, we can't see the dup line because
  566.        * cursor doesn't move and dup line is added after current line.
  567.        */
  568.       if  (window->cline != line) {
  569.          window_scroll_down( window->cline, line );
  570.          update_line( window );
  571.       }
  572.       file->dirty = NOT_LOCAL;
  573.  
  574.       /*
  575.        * record that file has been modified
  576.        */
  577.       file->modified = TRUE;
  578.       ++file->length;
  579.       restore_marked_block( window, 1 );
  580.       show_size( window );
  581.       show_avail_mem( );
  582.    } else
  583.       error( WARNING, line, "cannot duplicate line" );
  584. }
  585.  
  586.  
  587. /*
  588.  * Name:    back_space
  589.  * Purpose: To delete the character to the left of the cursor.
  590.  * Date:    June 5, 1991
  591.  * Passed:  window:   information allowing access to the current window
  592.  * Notes:   If the cursor is at the left of the line, then combine the
  593.  *           current line with the previous one.
  594.  *          If in indent mode, and the cursor is on the first non-blank
  595.  *           character of the line, then match the indentation of an
  596.  *           earlier line.
  597.  */
  598. void back_space( window )
  599. windows *window;
  600. {
  601. int len;            /* length of the current line */
  602. text_ptr source;    /* source of block move to delete character */
  603. text_ptr dest;      /* destination of block move */
  604. long number;        /* number of characters to move */
  605. text_ptr p;         /* previous line in file */
  606. int cr;             /* did line end with carriage return? */
  607. int plen;           /* length of previous line */
  608. int del_count;      /* number of characters to delete */
  609. int pos;            /* the position of the first non-blank char */
  610. int prompt_line;    /* line on screen to save and show prompt */
  611. int rcol, ccol, ncols;
  612. file_infos *file;
  613.  
  614.    file = window->file_info;
  615.    prompt_line = window->bottom_line;
  616.    copy_line( window->cursor, prompt_line );
  617.    len = linelen( g_status.line_buff );
  618.    rcol = window->rcol;
  619.    ccol = window->ccol;
  620.    ncols = g_display.ncols;
  621.    if (rcol == 0) {
  622.       /*
  623.        * combine this line with the previous, if any
  624.        */
  625.       window->cursor = cpb( window->cursor );
  626.       if ((p = find_prev( window->cursor )) != NULL) {
  627.          if (len + 2 + (plen = linelen( p )) >= g_display.line_length) {
  628.             error( WARNING, prompt_line, "cannot combine lines" );
  629.             return;
  630.          }
  631.  
  632.          un_copy_line( window->cursor, window, TRUE );
  633.          copy_line( p, prompt_line );
  634.          g_status.line_buff[plen] = CONTROL_Z;
  635.          window->cursor = p;
  636.          un_copy_line( window->cursor, window, FALSE );
  637.  
  638.          /*
  639.           * make sure cursor stays on the screen, at the end of the
  640.           *  previous line
  641.           */
  642.          if (window->cline > window->top_line)
  643.             --window->cline;
  644.          --window->rline;
  645.          rcol = plen;
  646.          ccol = rcol - window->bcol;
  647.          --file->length;
  648.          restore_marked_block( window, -1 );
  649.          adjust_windows_cursor( window, -1 );
  650.          show_size( window );
  651.          check_virtual_col( window, rcol, ccol );
  652.          file->dirty = GLOBAL;
  653.       }
  654.    } else {
  655.       /*
  656.        * normal delete
  657.        *
  658.        * find out how much to delete (depends on indent mode)
  659.        */
  660.       del_count = 1;   /* the default */
  661.       if (g_status.indent) {
  662.          /*
  663.           * indent only happens if the cursor is on the first
  664.           *  non-blank character of the line
  665.           */
  666.          if ((pos = first_non_blank( g_status.line_buff )) == rcol
  667.                     || g_status.line_buff[pos] == '\n'
  668.                     || g_status.line_buff[pos] == CONTROL_Z) {
  669.             /*
  670.              * now work out how much to indent
  671.              */
  672.             p = cpb( window->cursor );
  673.             for (p=find_prev( p ); p != NULL; p=find_prev( p )) {
  674.                if ((plen = first_non_blank( (char *)p )) < rcol &&
  675.                     *(p+plen) != '\n') {
  676.                   /*
  677.                    * found the line to match
  678.                    */
  679.                   del_count = rcol - plen;
  680.                   break;
  681.                }
  682.             }
  683.          }
  684.       }
  685.  
  686.       /*
  687.        * move text to delete char(s), unless no chars actually there
  688.        */
  689.       if (rcol - del_count < len) {
  690.          dest = g_status.line_buff + rcol - del_count;
  691.          if (rcol > len) {
  692.             source = g_status.line_buff + len;
  693.             len = 2;
  694.          } else {
  695.             source = g_status.line_buff + rcol;
  696.             len = len - rcol + 2;
  697.          }
  698.          memmove( dest, source, len );
  699.       }
  700.       rcol -= del_count;
  701.       ccol -= del_count;
  702.       check_virtual_col( window, rcol, ccol );
  703.       if (file->dirty) {
  704.          display_current_window( window );
  705.          file->dirty = NOT_LOCAL;
  706.       } else
  707.          file->dirty = GLOBAL;
  708.       show_changed_line( window );
  709.    }
  710.    file->modified = TRUE;
  711. }
  712.  
  713.  
  714. /*
  715.  * Name:    line_kill
  716.  * Purpose: To delete the line the cursor is on.
  717.  * Date:    June 5, 1991
  718.  * Passed:  window:   information allowing access to the current window
  719.  * Notes:   If *window->cursor is pointing to CONTROL_Z then do not do a
  720.  *          line kill (can't kill a NULL line).
  721.  */
  722. void line_kill( window )
  723. windows *window;
  724. {
  725. int i = 0;
  726. text_ptr s;         /* next line in file */
  727. windows w;
  728. file_infos *file;
  729.  
  730.    file = window->file_info;
  731.    if (file->length > 0  && *window->cursor != CONTROL_Z) {
  732.  
  733.       g_status.copied = TRUE;
  734.       g_status.line_buff[0] = CONTROL_Z;
  735.       s = window->cursor = cpf( window->cursor );
  736.       /*
  737.        * if line to delete has \n at end of line then decrement file length.
  738.        */
  739.       if (*(s + linelen( s )) == '\n') {
  740.          --file->length;
  741.          --i;
  742.       }
  743.       un_copy_line( s, window, FALSE );
  744.       file->dirty = NOT_LOCAL;
  745.  
  746.       /*
  747.        * move all cursors one according to i, restore begin and end block
  748.        */
  749.       adjust_windows_cursor( window, i );
  750.       restore_marked_block( window, i );
  751.  
  752.       /*
  753.        * we are not doing a GLOBAL update, so update current window here
  754.        */
  755.       if (file->dirty == NOT_LOCAL) {
  756.          window_scroll_up( window->cline, window->bottom_line );
  757.          dup_window_info( &w, window );
  758.          for (; w.cursor != NULL; w.cline++, w.rline++) {
  759.             if (w.cline == w.bottom_line) {
  760.                update_line( &w );
  761.                break;
  762.             }
  763.             w.cursor = find_next( w.cursor );
  764.          }
  765.       }
  766.       show_size( window );
  767.    }
  768. }
  769.  
  770.  
  771. /*
  772.  * Name:    char_del_under
  773.  * Purpose: To delete the character under the cursor.
  774.  * Date:    June 5, 1991
  775.  * Passed:  window:   information allowing access to the current window
  776.  * Notes:   If the cursor is beyond the end of the line, then this
  777.  *           command is ignored.
  778.  */
  779. void char_del_under( window )
  780. windows *window;
  781. {
  782. text_ptr source;    /* source of block move to delete character */
  783. int len;
  784. int rcol;
  785.  
  786.    copy_line( window->cursor, window->bottom_line );
  787.    rcol = window->rcol;
  788.    if (rcol < (len = linelen( g_status.line_buff ))) {
  789.       /*
  790.        * move text to delete char using buffer
  791.        */
  792.       source = g_status.line_buff + rcol + 1;
  793.       memmove( source-1, source, len-rcol+2 );
  794.       window->file_info->dirty = GLOBAL;
  795.       show_changed_line( window );
  796.    }
  797. }
  798.  
  799.  
  800. /*
  801.  * Name:    eol_kill
  802.  * Purpose: To delete everything from the cursor to the end of the line.
  803.  * Date:    June 5, 1991
  804.  * Passed:  window:   information allowing access to the current window
  805.  * Notes:   If the cursor is beyond the end of the line, then this
  806.  *           command is ignored.
  807.  */
  808. void eol_kill( window )
  809. windows *window;
  810. {
  811. char *dest;  /* the start of the delete area */
  812.  
  813.    copy_line( window->cursor, window->bottom_line );
  814.    if (window->rcol < linelen( g_status.line_buff )) {
  815.       /*
  816.        * truncate to delete rest of line
  817.        */
  818.       dest = g_status.line_buff + window->rcol;
  819.       *dest++ = '\n';
  820.       *dest = CONTROL_Z;
  821.       window->file_info->dirty = GLOBAL;
  822.       show_changed_line( window );
  823.    }
  824. }
  825.  
  826.  
  827. /*
  828.  * Name:    undo_line
  829.  * Purpose: To retrieve unaltered line if possible.
  830.  * Date:    June 5, 1991
  831.  * Passed:  window:   information allowing access to the current window
  832.  * Notes:   Changes are made to the line buffer so the underlying text has
  833.  *          not changed.  put the unchanged line in the line buffer.
  834.  */
  835. void undo_line( window )
  836. windows *window;
  837. {
  838.  
  839.    if (g_status.copied) {
  840.       g_status.copied = FALSE;
  841.       copy_line( window->cursor, window->bottom_line );
  842.       window->file_info->dirty = GLOBAL;
  843.       show_changed_line( window );
  844.    }
  845. }
  846.  
  847.  
  848. /*
  849.  * Name:    goto_left
  850.  * Purpose: To move the cursor to the left of the current line.
  851.  * Date:    June 5, 1991
  852.  * Passed:  window:   information allowing access to the current window
  853.  */
  854. void goto_left( window )
  855. windows *window;
  856. {
  857. int rcol;
  858.  
  859.    if (g_status.copied)
  860.       rcol = first_non_blank( g_status.line_buff );
  861.    else
  862.       rcol = first_non_blank( window->cursor );
  863.    if (window->cursor[rcol] == '\n')
  864.       rcol = 0;
  865.    check_virtual_col( window, rcol, window->ccol );
  866. }
  867.  
  868.  
  869. /*
  870.  * Name:    goto_right
  871.  * Purpose: To move the cursor to the right of the current line.
  872.  * Date:    June 5, 1991
  873.  * Passed:  window:   information allowing access to the current window
  874.  */
  875. void goto_right( window )
  876. windows *window;
  877. {
  878. int rcol;
  879.  
  880.    if (g_status.copied)
  881.       rcol = linelen( g_status.line_buff );
  882.    else
  883.       rcol = linelen( window->cursor );
  884.    window->ccol = rcol - window->bcol;
  885.    check_virtual_col( window, rcol, window->ccol );
  886. }
  887.  
  888.  
  889. /*
  890.  * Name:    goto_top
  891.  * Purpose: To move the cursor to the top of the current window.
  892.  * Date:    June 5, 1991
  893.  * Passed:  window:   information allowing access to the current window
  894.  * Notes:   If the start of the file occurs before the top of the window,
  895.  *           then the start of the file is moved to the top of the window.
  896.  */
  897. void goto_top( window )
  898. windows *window;
  899. {
  900. text_ptr cursor;  /* anticipated cursor line */
  901.  
  902.    un_copy_line( window->cursor, window, TRUE );
  903.    window->cursor = cpb( window->cursor );
  904.    for (; window->cline > window->top_line; window->cline--,window->rline--) {
  905.       if ((cursor = find_prev( window->cursor )) == NULL)
  906.          break;
  907.       window->cursor = cursor;
  908.    }
  909. }
  910.  
  911.  
  912. /*
  913.  * Name:    goto_bottom
  914.  * Purpose: To move the cursor to the bottom of the current window.
  915.  * Date:    June 5, 1991
  916.  * Passed:  window:   information allowing access to the current window
  917.  */
  918. void goto_bottom( window )
  919. windows *window;
  920. {
  921. text_ptr cursor;
  922.  
  923.    un_copy_line( window->cursor, window, TRUE );
  924.    window->cursor = cpf( window->cursor );
  925.    for (; window->cline < window->bottom_line; window->cline++,window->rline++) {
  926.       if ((cursor = find_next( window->cursor )) == NULL)
  927.          break;
  928.       window->cursor = cursor;
  929.    }
  930. }
  931.  
  932.  
  933. /*
  934.  * Name:    set_tabstop
  935.  * Purpose: To set the current interval between tab stops
  936.  * Date:    October 1, 1989
  937.  * Notes:   Tab interval must be reasonable, and this function will
  938.  *           not allow tabs more than MAX_COLS / 2.
  939.  */
  940. void set_tabstop( line )
  941. int line;
  942. {
  943. char num_str[MAX_COLS];  /* tab interval as a character string */
  944. int tab;                 /* new tab interval */
  945. int rc;
  946.  
  947.    rc = OK;
  948.    while (rc == OK) {
  949.       itoa( g_status.tab_size, num_str, 10 );
  950.       rc = get_name( "Tab interval: ", line, num_str, g_display.message_color );
  951.       if (rc == OK) {
  952.          tab = atoi( num_str );
  953.          if (tab < MAX_COLS/2) {
  954.             g_status.tab_size = tab;
  955.             rc = ERROR;
  956.          } else
  957.             error( WARNING, line, "tab size too long" );
  958.       }
  959.    }
  960. }
  961.  
  962.  
  963. /*
  964.  * Name:    show_line_col
  965.  * Purpose: show current real line and column of current cursor position
  966.  * Date:    June 5, 1991
  967.  * Passed:  window:   information allowing access to the current window
  968.  * Notes:   Blank old position and display new position.  current line and
  969.  *          column may take up to 12 columns, which allows the display of
  970.  *          999 columns and 99,999,999 lines.
  971.  */
  972. void show_line_col( windows *window )
  973. {
  974. int i, k, pline;
  975. char line_col[20], num[10];
  976.  
  977.    /*
  978.     * blank out current line:column position.
  979.     */
  980.    strcpy( line_col, "            " );
  981.  
  982.    /*
  983.     * convert column to ascii and store in display buffer.
  984.     */
  985.    itoa( window->rcol+1, num, 10 );
  986.    i = strlen( num ) - 1;
  987.    for (k=11; i>=0; i--, k--)
  988.       line_col[k] = num[i];
  989.  
  990.    /*
  991.     * put in colon to separate line and column
  992.     */
  993.    line_col[k--] = ':';
  994.  
  995.    /*
  996.     * convert line to ascii and store in display buffer.
  997.     */
  998.    ltoa( window->rline, num, 10 );
  999.    i = strlen( num ) - 1;
  1000.    for (; i>=0; i--, k--)
  1001.       line_col[k] = num[i];
  1002.  
  1003.    /*
  1004.     * find line to start line:column display then output
  1005.     */
  1006.    pline = window->top_line - 1;
  1007.    s_output( line_col, pline, MAX_COLS-12, g_display.head_color );
  1008. }
  1009.  
  1010.  
  1011. /*
  1012.  * Name:    command
  1013.  * Purpose: To input and execute a command or printable character.
  1014.  * Date:    June 5, 1991
  1015.  * Passed:  window:   information allowing access to the current window
  1016.  * Notes:   Dispatch editor functions as needed.
  1017.  */
  1018. void command( window, stop )
  1019. windows *window;
  1020. int *stop;
  1021. {
  1022. int c;      /* character entered */
  1023. int temp;   /* temp variable */
  1024. int func;
  1025.  
  1026.    show_line_col( window );
  1027.    c = getkey( );
  1028.    func = getfunc( c );
  1029.    s_output( "          ", g_display.mode_line, 63, g_display.mode_color );
  1030.    switch (func) {
  1031.       case Tab :
  1032.          tab_key( window );
  1033.          break;
  1034.       case BackSpace :
  1035.          back_space( window );
  1036.          break;
  1037.       case DeleteChar :
  1038.          char_del_under( window );
  1039.          break;
  1040.       case Rturn :
  1041.          insert_newline( window, TRUE, FALSE );
  1042.          break;
  1043.       case AddLine :
  1044.          insert_newline( window, FALSE, FALSE );
  1045.          break;
  1046.       case SplitLine :
  1047.          insert_newline( window, TRUE, TRUE );
  1048.          break;
  1049.       case JoinLine :
  1050.          join_line( window );
  1051.          break;
  1052.       case DuplicateLine :
  1053.          dup_line( window );
  1054.          break;
  1055.       case AbortCommand :
  1056.       case UndoLine :
  1057.          undo_line( window );
  1058.          break;
  1059.       case ScrollUpLine :
  1060.          scroll_up( window );
  1061.          break;
  1062.       case ScrollDnLine :
  1063.          scroll_down( window );
  1064.          break;
  1065.       case LineDown :
  1066.          move_down( window );
  1067.          break;
  1068.       case LineUp :
  1069.          move_up( window );
  1070.          break;
  1071.       case CharLeft :
  1072.          move_left( window );
  1073.          break;
  1074.       case CharRight :
  1075.          move_right( window );
  1076.          break;
  1077.       case ScreenDown :
  1078.          page_down( window );
  1079.          break;
  1080.       case ScreenUp :
  1081.          page_up( window );
  1082.          break;
  1083.       case WordRight :
  1084.          word_right( window );
  1085.          break;
  1086.       case WordLeft :
  1087.          word_left( window );
  1088.          break;
  1089.       case DeleteLine :
  1090.          line_kill( window );
  1091.          break;
  1092.       case OverWrite :
  1093.          g_status.insert = !g_status.insert;
  1094.          show_modes( );
  1095.          if (g_status.insert)
  1096.             temp = g_display.insert_cursor;
  1097.          else
  1098.             temp = g_display.overw_cursor;
  1099.          set_cursor_size( temp );
  1100.          break;
  1101.       case Help :
  1102.          get_help( window );
  1103.          break;
  1104.       case RedrawScreen :
  1105.          force_blank( );
  1106.          redraw_screen( window );
  1107.          break;
  1108.       case Quit :
  1109.          quit( window, stop );
  1110.          break;
  1111.       case Save :
  1112.          save_file( window );
  1113.          break;
  1114.       case SaveAs :
  1115.          save_as_file( window );
  1116.          break;
  1117.       case File :
  1118.          un_copy_line( window->cursor, window, TRUE );
  1119.          if (window->file_info->modified)
  1120.             save_file( window );
  1121.          finish( window, stop );
  1122.          break;
  1123.       case EditFile :
  1124.          edit_another_file( window );
  1125.          break;
  1126.       case UnMarkBlock :
  1127.          unmark_block( );
  1128.          break;
  1129.       case MarkBlock :
  1130.          mark_block( window, BOX );
  1131.          break;
  1132.       case MarkLine :
  1133.          mark_block( window, LINE );
  1134.          break;
  1135.       case MoveBlock :
  1136.          if (g_status.marked == TRUE)
  1137.             move_copy_delete_overlay_block( window, MOVE );
  1138.          break;
  1139.       case DeleteBlock :
  1140.          if (g_status.marked == TRUE)
  1141.             move_copy_delete_overlay_block( window, DELETE );
  1142.          break;
  1143.       case CopyBlock :
  1144.          if (g_status.marked == TRUE)
  1145.             move_copy_delete_overlay_block( window, COPY );
  1146.          break;
  1147.       case KopyBlock :
  1148.          if (g_status.marked == TRUE)
  1149.             move_copy_delete_overlay_block( window, KOPY );
  1150.          break;
  1151.       case FillBlock :
  1152.          if (g_status.marked == TRUE)
  1153.             move_copy_delete_overlay_block( window, FILL );
  1154.          break;
  1155.       case OverlayBlock :
  1156.          if (g_status.marked == TRUE)
  1157.             move_copy_delete_overlay_block( window, OVERLAY );
  1158.          break;
  1159.       case BlockToFile :
  1160.          block_write( window );
  1161.          break;
  1162.       case PrintBlock :
  1163.          block_print( window );
  1164.          break;
  1165.       case BlockExpandTabs :
  1166.          block_expand_tabs( window );
  1167.          break;
  1168.       case DelEndOfLine :
  1169.          eol_kill( window );
  1170.          break;
  1171.       case FindForward :
  1172.          find_string( window, FORWARD, TRUE );
  1173.          break;
  1174.       case FindBackward :
  1175.          find_string( window, BACKWARD, TRUE );
  1176.          break;
  1177.       case RepeatFindForward :
  1178.          find_string( window, FORWARD, FALSE );
  1179.          break;
  1180.       case RepeatFindBackward :
  1181.          find_string( window, BACKWARD, FALSE );
  1182.          break;
  1183.       case ReplaceForward :
  1184.          replace_string( window, FORWARD );
  1185.          break;
  1186.       case ReplaceBackward :
  1187.          replace_string( window, BACKWARD );
  1188.          break;
  1189.       case BegOfLine :
  1190.          goto_left( window );
  1191.          break;
  1192.       case EndOfLine :
  1193.          goto_right( window );
  1194.          break;
  1195.       case TopOfScreen :
  1196.          goto_top( window );
  1197.          break;
  1198.       case BotOfScreen :
  1199.          goto_bottom( window );
  1200.          break;
  1201.       case TopOfFile :
  1202.          goto_top_file( window );
  1203.          break;
  1204.       case EndOfFile :
  1205.          goto_end_file( window );
  1206.          break;
  1207.       case ParenBalance :
  1208.          match_pair( window );
  1209.          break;
  1210.       case JumpToLine :
  1211.          goto_line( window );
  1212.          break;
  1213.       case Indent :
  1214.          g_status.indent = !g_status.indent;
  1215.          show_indent_mode( );
  1216.          break;
  1217.       case ToggleSearchCase :
  1218.          if (bm.search_case == IGNORE)
  1219.             bm.search_case = MATCH;
  1220.          else
  1221.             bm.search_case = IGNORE;
  1222.          show_search_case( );
  1223.          break;
  1224.       case SetTabs :
  1225.          set_tabstop( window->bottom_line );
  1226.          break;
  1227.       case NextWindow :
  1228.          next_window( window );
  1229.          break;
  1230.       case PreviousWindow :
  1231.          prev_window( window );
  1232.          break;
  1233.       case SplitScreen :
  1234.          split_screen( window );
  1235.          break;
  1236.       case SizeWindow :
  1237.          size_window( window );
  1238.          break;
  1239.       case Column_1 :
  1240.          column_1( window );
  1241.          break;
  1242.       default :
  1243.          if (c < 0x100)
  1244.             insert_overwrite( window, c );
  1245.          break;
  1246.    }
  1247. }
  1248.  
  1249.  
  1250. /*
  1251.  * Name:    editor
  1252.  * Purpose: Set up the editor structures and display changes as needed.
  1253.  * Date:    June 5, 1991
  1254.  * Passed:  argc:   number of command line arguments
  1255.  *          argv:   text of command line arguments
  1256.  */
  1257. void editor( argc, argv )
  1258. int argc;
  1259. char *argv[];
  1260. {
  1261. char *name;  /* name of file to start editing */
  1262. windows *window;            /* current active window */
  1263. windows *above;             /* window above current */
  1264. windows *below;             /* window below current */
  1265. file_infos *file;           /* temporary file structure */
  1266. char status_line[MAX_COLS+2]; /* status line at top of window */
  1267. char *p, *q;        /* for setting up status line */
  1268. int temp;
  1269. int stop;
  1270.  
  1271.    /*
  1272.     * set up the screen
  1273.     */
  1274.    initialize( );
  1275.  
  1276.    /*
  1277.     * Check that user specified file to edit, if not offer help
  1278.     */
  1279.    if (argc > 1)
  1280.       name = argv[1];
  1281.    else {
  1282.       g_status.rw_name[0] = '\0';
  1283.       name = g_status.rw_name;
  1284.       if (get_name( "File name to edit : ", g_display.nlines, name,
  1285.                     g_display.text_color ) != OK)
  1286.          return;
  1287.       if (*name == '\0')
  1288.          return;
  1289.    }
  1290.  
  1291.    edit_file( name );
  1292.    if (initialize_window( ) != ERROR) {
  1293.       stop = FALSE;
  1294.       window = g_status.current_window;
  1295.       window->file_info->dirty = FALSE;
  1296.       show_window_header( window->file_info->file_name, window );
  1297.       show_size_name( window );
  1298.       show_size( window );
  1299.  
  1300.       /*
  1301.        * display the current window
  1302.        */
  1303.       display_current_window( window );
  1304.       show_modes( );
  1305.       if (g_status.insert)
  1306.          temp = g_display.insert_cursor;
  1307.       else
  1308.          temp = g_display.overw_cursor;
  1309.       set_cursor_size( temp );
  1310.    } else
  1311.       stop = TRUE;
  1312.  
  1313.    /*
  1314.     * main loop - keep updating the display and processing any commands
  1315.     *  while user has not pressed stop
  1316.     */
  1317.    for (; stop != TRUE;) {
  1318.       window = g_status.current_window;
  1319.  
  1320.       /*
  1321.        * update all the other windows that point to file that has been changed
  1322.        */
  1323.       above = below = window;
  1324.       while (above->prev || below->next) {
  1325.          if (above->prev) {
  1326.             above = above->prev;
  1327.             if (above->visible) {
  1328.                file = above->file_info;
  1329.                if (file->dirty == GLOBAL || file->dirty == NOT_LOCAL) {
  1330.                   display_current_window( above );
  1331.                   show_size( above );
  1332.                }
  1333.             }
  1334.          }
  1335.          if (below->next) {
  1336.             below = below->next;
  1337.             if (below->visible) {
  1338.                file = below->file_info;
  1339.                if (file->dirty == GLOBAL || file->dirty == NOT_LOCAL) {
  1340.                   display_current_window( below );
  1341.                   show_size( below );
  1342.                }
  1343.             }
  1344.          }
  1345.       }
  1346.       file = window->file_info;
  1347.       if (file->dirty == LOCAL || file->dirty == GLOBAL)
  1348.          display_current_window( window );
  1349.       file->dirty = FALSE;
  1350.  
  1351.       /*
  1352.        * all done, so position the cursor and wait for the user to enter
  1353.        *  something
  1354.        */
  1355.       xygoto( window->ccol, window->cline );
  1356.       command( window, &stop );
  1357.    }
  1358.    terminate( );
  1359. }
  1360.